﻿using Douyu.Common;
using Douyu.SDK.Messages;
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Douyu.SDK
{
    /// <summary>
    /// 斗鱼客户端类
    /// </summary>
    public class DouyuSocketClient
    {
        /// <summary>
        /// 斗鱼服务器地址
        /// </summary>
        public string Address { get; private set; }

        /// <summary>
        /// 斗鱼服务器端口
        /// </summary>
        public int Port { get; private set; }

        /// <summary>
        /// 斗鱼Tcp客户端
        /// </summary>
        protected TcpClient Client { get; private set; }

        /// <summary>
        /// 最后一次收到消息的时间
        /// </summary>
        public DateTime LastReceiveMsgTime { get; private set; } = DateTime.MinValue;

        /// <summary>
        /// 房间ID
        /// </summary>
        public int RoomId { get; private set; } = 0;

        /// <summary>
        /// 是否连接上了
        /// </summary>
        public bool IsConnected
        {
            get
            {
                return Client != null && Client.Connected;
            }
        }

        /// <summary>
        /// 斗鱼客户端
        /// </summary>
        /// <param name="address">斗鱼服务器地址(默认:openbarrage.douyutv.com)</param>
        /// <param name="port">斗鱼服务器IP(默认;8602)</param>
        public DouyuSocketClient(string address = "openbarrage.douyutv.com", int port = 8602)
        {
            this.Address = address;
            this.Port = port;
        }

        /// <summary>
        /// 客户端发生错误时
        /// </summary>
        public event EventHandler<Exception> OnError;

        #region 启动客户端

        /// <summary>
        /// 启动客户端
        /// </summary>
        public async Task ConnectAsync()
        {
            try
            {
                Client = new TcpClient();
                await Client.ConnectAsync(this.Address, this.Port);
                await Task.Delay(100);
                KeepLive();
                DoReceive();
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 登录(roomid)

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="roomid"></param>
        /// <returns></returns>
        public void Login(int roomid)
        {
            if (!IsConnected) return;
            try
            {
                var msg = new LoginRequest { roomid = roomid };
                this.RoomId = roomid;
                SendMsg(msg);
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 入组消息

        /// <summary>
        /// 入组消息
        /// </summary>
        public void JoinGroup()
        {
            try
            {
                var msg = new JoinGroupMessage { rid = this.RoomId };
                SendMsg(msg);
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 登出

        /// <summary>
        /// 登出
        /// </summary>
        public void Logout()
        {
            if (!IsConnected) return;
            try
            {
                var msg = new LogoutMessage();
                SendMsg(msg);
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 发送心跳

        /// <summary>
        /// 发送心跳包
        /// </summary>
        public async void KeepLive()
        {
            while (IsConnected)
            {
                var keeplive = new KeepLiveMessage();
                keeplive.tick = DateTime.Now.ToUnixTime();
                SendMsg(keeplive);
                await Task.Delay(TimeSpan.FromSeconds(30));
            }
        }

        #endregion

        #region 消息事件

        /// <summary>
        /// 接收到消息时
        /// </summary>
        public event EventHandler<BaseMessage> OnReceiveMessage;

        /// <summary>
        /// 收到一个未知类型的数据包
        /// </summary>
        public event EventHandler<BaseMessage> OnReceiveUnknowMessage;

        /// <summary>
        /// 收到服务器登录请求响应
        /// </summary>
        public event EventHandler<LoginResponse> OnReceiveLoginResponse;

        /// <summary>
        /// 收到弹幕消息
        /// </summary>
        public event EventHandler<ChatMessage> OnReceiveChatMessage;

        /// <summary>
        /// 收到错误消息
        /// </summary>
        public event EventHandler<ErrorMessage> OnReceiveErrorMessage;

        /// <summary>
        /// 收到服务器心跳响应
        /// </summary>
        public event EventHandler<KeepLiveMessage> OnReceiveKeepLiveMessage;

        /// <summary>
        /// 用户进入房间通知消息
        /// </summary>
        public event EventHandler<UserEnterMessage> OnReceiveUserEnterMessage;

        /// <summary>
        /// 赠送礼物消息
        /// </summary>
        public event EventHandler<DgbMessage> OnReceiveDgbMessage;

        /// <summary>
        /// 领取在线鱼丸爆击消息
        /// </summary>
        public event EventHandler<OnlineGiftMessage> OnReceiveOnlineGiftMessage;

        /// <summary>
        /// 个人用户获取头衔的信息
        /// </summary>
        public event EventHandler<GiftTitleMessage> OnReceiveGiftTitleMessage;

        /// <summary>
        /// 用户赠送酬勤通知消息
        /// </summary>
        public event EventHandler<BcBuyDeserveMessage> OnReceiveBcBuyDeserveMessage;

        /// <summary>
        /// 刷火箭通知消息(??)
        /// </summary>
        public event EventHandler<SpbcMessage> OnReceiveSpbcMessage;

        /// <summary>
        /// 排行榜变更通知信息
        /// </summary>
        public event EventHandler<RankListMessage> OnReceiveRankListMessage;

        /// <summary>
        /// 个人用户的升级信息
        /// </summary>
        public event EventHandler<UpgradeMessage> OnReceiveUpgradeMessage;

        /// <summary>
        /// 收到RankUpMessage
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<RankUpMessage> OnReceiveRankUpMessage;

        /// <summary>
        /// 收到:DonateresMessage
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<DonateresMessage> OnReceiveDonateresMessage;

        /// <summary>
        /// 收到:BlackResponse
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<BlackResponse> OnReceiveBlackResponse;

        /// <summary>
        /// 收到GgbbMessage
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<GgbbMessage> OnReceiveGgbbMessage;

        /// <summary>
        /// 收到rsses
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<SrResponse> OnReceiveSrResponse;

        /// <summary>
        /// 收到RssMessage
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<RssMessage> OnReceiveRssMessage;

        /// <summary>
        /// 收到NewBlackResponse
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<NewBlackResponse> OnReceiveNewBlackResponse;

        /// <summary>
        /// 收到UlRankListMessage
        /// TODO:消息用途猜测中
        /// </summary>
        public event EventHandler<UlRankListMessage> OnReceiveUlRankListMessage;

        /// <summary>
        /// 连接被关闭消息
        /// </summary>
        public event EventHandler<TcpClient> OnClose;

        #endregion

        #region 处理消息

        /// <summary>
        /// 处理一个消息
        /// </summary>
        /// <param name="msg"></param>
        private async void ProcessMessage(BaseMessage msg)
        {
            try
            {
                await Task.Delay(0);
                OnReceiveMessage?.Invoke(this, msg);
                switch (msg.type)
                {
                    case MessageType.loginres:
                        {
                            OnReceiveLoginResponse?.Invoke(this, new LoginResponse(msg));
                            break;
                        }
                    case MessageType.chatmsg:
                        {
                            OnReceiveChatMessage?.Invoke(this, new ChatMessage(msg));
                            break;
                        }
                    case MessageType.uenter:
                        {
                            OnReceiveUserEnterMessage?.Invoke(this, new UserEnterMessage(msg));
                            break;
                        }
                    case MessageType.keeplive:
                        {
                            OnReceiveKeepLiveMessage?.Invoke(this, new KeepLiveMessage(msg));
                            break;
                        }
                    case MessageType.dgb:
                        {
                            OnReceiveDgbMessage?.Invoke(this, new DgbMessage(msg));
                            break;
                        }
                    case MessageType.upgrade:
                        {
                            OnReceiveUpgradeMessage?.Invoke(this, new UpgradeMessage(msg));
                            break;
                        }
                    case MessageType.onlinegift:
                        {
                            OnReceiveOnlineGiftMessage?.Invoke(this, new OnlineGiftMessage(msg));
                            break;
                        }
                    case MessageType.gift_title:
                        {
                            OnReceiveGiftTitleMessage?.Invoke(this, new GiftTitleMessage(msg));
                            break;
                        }
                    case MessageType.spbc:
                        {
                            OnReceiveSpbcMessage?.Invoke(this, new SpbcMessage(msg));
                            break;
                        }
                    case MessageType.bc_buy_deserve:
                        {
                            OnReceiveBcBuyDeserveMessage?.Invoke(this, new BcBuyDeserveMessage(msg));
                            break;
                        }
                    case MessageType.ranklist:
                        {
                            OnReceiveRankListMessage?.Invoke(this, new RankListMessage(msg));
                            break;
                        }
                    case MessageType.rankup:
                        {
                            OnReceiveRankUpMessage?.Invoke(this, new RankUpMessage(msg));
                            break;
                        }
                    case MessageType.ggbb:
                        {
                            OnReceiveGgbbMessage?.Invoke(this, new GgbbMessage(msg));
                            break;
                        }
                    case MessageType.donateres:
                        {
                            OnReceiveDonateresMessage(this, new DonateresMessage(msg));
                            break;
                        }
                    case MessageType.blackres:
                        {
                            OnReceiveBlackResponse?.Invoke(this, new BlackResponse(msg));
                            break;
                        }
                    case MessageType.srses:
                        {
                            OnReceiveSrResponse?.Invoke(this, new SrResponse(msg));
                            break;
                        }
                    case MessageType.rss:
                        {
                            OnReceiveRssMessage?.Invoke(this, new RssMessage(msg));
                            break;
                        }
                    case MessageType.newblackres:
                        {
                            OnReceiveNewBlackResponse?.Invoke(this, new NewBlackResponse(msg));
                            break;
                        }
                    case MessageType.ul_ranklist:
                        {
                            OnReceiveUlRankListMessage(this, new UlRankListMessage(msg));
                            break;
                        }
                    case MessageType.error:
                        {
                            OnReceiveErrorMessage?.Invoke(this, new ErrorMessage(msg));
                            break;
                        }
                    default:
                        {
                            OnReceiveUnknowMessage?.Invoke(this, new UnknowMessage(msg));
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 接收信息

        /// <summary>
        /// 接收信息
        /// </summary>
        private async void DoReceive()
        {
            if (!IsConnected) return;

            var ns = this.Client.GetStream();

            try
            {
                while (IsConnected)
                {
                    if (ns.DataAvailable)
                    {
                        await ReceiveMsg(ns);
                    }
                    await Task.Delay(1);
                }
                OnClose?.Invoke(this, Client);
            }
            catch (Exception ex)
            {
                this.Client.Close();
                this.Client = null;
                OnError?.Invoke(this, ex);
                OnClose?.Invoke(this, Client);
            }
        }

        #endregion

        #region 接收数据

        /// <summary>
        /// 接收一个数据包
        /// </summary>
        /// <param name="ns"></param>
        private async Task ReceiveMsg(NetworkStream ns)
        {
            try
            {
                await Task.Delay(0);
                lock (ns)
                {
                    var br = new BinaryReader(ns);
                    var msg = new BaseMessage();

                    msg.MsgType = MsgType.ServerToClient;
                    msg.Len = br.ReadInt32();
                    msg.Len2 = br.ReadInt32();
                    msg.MsgType = (MsgType)br.ReadInt16();
                    msg.EncodeKey = br.ReadByte();
                    msg.Tag = br.ReadByte();

                    var datas = br.ReadBytes(msg.Len - 9);
                    var endchar = br.ReadChar();
                    var msgBody = Encoding.UTF8.GetString(datas);
                    var msg2 = STTSerializer.Deserialize(msg, msgBody);
                    ProcessMessage(msg2);
                }
            }
            catch (Exception ex)
            {
                OnError?.Invoke(this, ex);
            }
        }

        #endregion

        #region 发送消息

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="msg"></param>
        public async void SendMsg<T>(T msg)
            where T : BaseMessage
        {
            if (!IsConnected) return;
            try
            {
                var ns = this.Client.GetStream();
                var bw = new BinaryWriter(ns);
                var msgdata = STTSerializer.Serialize(msg);
                var datas = Encoding.UTF8.GetBytes(msgdata);
                var len = datas.Length + 9;

                bw.Write(len);
                bw.Write(len);
                bw.Write((short)msg.MsgType);
                bw.Write(msg.EncodeKey);
                bw.Write(msg.Tag);
                bw.Write(datas);
                bw.Write('\0');
                bw.Flush();

                await ns.FlushAsync();
            }
            catch (Exception ex)
            {
                this.OnError?.Invoke(this, ex);
            }
        }

        #endregion
    }

}
